﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using SongSharp.Appliance;
using SongSharp.Domain.Objects;
using SongSharp.Presentation.Command;
using SongSharp.Presentation.Properties;
using SongSharp.Presentation.View;
using SongSharp.Presentation.ViewModel.Data;

namespace SongSharp.Presentation.ViewModel
{
    public class PrimeViewModel : ViewModelBase, ISongSharpWindowCommands
    {
        // View bound to this model
        private PrimeView PrimeView { get; set; }

        // Other ViewModels
        private LogViewModel LogViewModel { get; set; }
        private ThinkViewModel ThinkViewModel { get; set; }
        private StatisticViewModel StatisticViewModel { get; set; }
        private WaitViewModel WaitViewModel { get; set; }

        // Commands from view PrimeView
        //private DelegateCommand clearContactBookCommand;
        private DelegateCommand m_exitCommand;
        private DelegateCommand m_playTrackCommand;
        private DelegateCommand m_enqueueTrackCommand;
        private DelegateCommand m_showStatisticCommand;
        
        // Data members for data bound properties
        private string m_currentTrackInfo;
        private double m_trackPosition;
        private double m_currentVolume;
        private string m_trackViewMessage;

        // Workaround for slider events (to avoid actions on automatic movement)
        private bool SongTrackerIsAutomaticallyMoved = true;

        private PlayerAppInfo PlayerInfo { get; set; }

        // Members that are accessed via properties by view PrimeView for databinding
        private ObservableCollection<PlaylistViewData> m_playlist = null;
        private ObservableCollection<TrackViewData> m_trackList = null;
        
        // Tag crawler for collecting ID3 Tag info of displayed tracks
        private ID3TagCrawler TagCrawler { get; set; }
        public PrimeViewModel()
        {
            LoadPlaylists();
            SongSharpApp.InitState();

            PlayerInfo = PlayerAppInfo.Instance;

            // VIEWMODEL INIT
            // Initializes worker for collecting ID3 tags
            TagCrawler = new ID3TagCrawler(this);
            
            PlaylistViewData playlistViewData = null;

            if (SongSharpApp.States.CurrentPlaylist != null && m_playlist != null)
                playlistViewData = m_playlist.Where(p => p.PlaylistObject.Uid ==
                    SongSharpApp.States.CurrentPlaylist.Uid).FirstOrDefault();
            if (playlistViewData != null)
                LoadTracklist(playlistViewData.PlaylistObject);
            CurrentVolume = SongSharpApp.States.CurrentVolume;
            PlayerApp.Basic.VolumeChanged(null, CurrentVolume);

            if (SongSharpApp.States.CurrentPlaylist != null)
                LoadTracklist(SongSharpApp.States.CurrentPlaylist);

            // Instantiation of the main view and the key point of the databinding in the MVVM pattern
            PrimeView = new PrimeView();
            LogViewModel = new LogViewModel(PrimeView);
            ThinkViewModel = new ThinkViewModel(this, PrimeView);
            StatisticViewModel = new StatisticViewModel(this, PrimeView);
            WaitViewModel = new WaitViewModel(this, PrimeView);
            PrimeView.DataContext = this;
            PrimeView.Show();

            // Registers an event that fires upon track reload
            PrimeView.TrackView.ItemContainerGenerator.StatusChanged += new EventHandler(ItemContainerGenerator_StatusChanged);
            
            // Registers an event that fires upon Property changed in the appliance layer
            PlayerInfo.PropertyChanged += new PropertyChangedEventHandler(PlayerInfo_PropertyChanged);

            // Registers an event that fires upon another track is played by the application layer
            PlayerInfo.TrackStarted += new PlayerAppInfo.TrackStartedEventHandler(PlayerInfo_TrackStarted);
        }

        void PlayerInfo_TrackStarted(object sender, Domain.Events.TrackStartedEventArgs e)
        {
            SetCurrentlyPlayedTrack();
        }

        void PlayerInfo_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            switch (e.PropertyName)
            {
                case "CurrentTrackInfo":
                    CurrentTrackInfo = PlayerInfo.CurrentTrackInfo;
                    break;
                case "TrackPosition":
                    TrackPosition = PlayerInfo.TrackPosition;
                    SongTrackerIsAutomaticallyMoved = true;
                    break;
                case "Time":
                    if (PlayerInfo.Time.Hour > 0)
                        CurrentTrackInfo = PlayerInfo.CurrentTrackInfo + ", " + PlayerInfo.Time.ToString("HH:mm:ss");
                    else
                        CurrentTrackInfo = PlayerInfo.CurrentTrackInfo + ", " + PlayerInfo.Time.ToString("mm:ss");
                    break;
            }
        }

        // fires upon track reload
        void ItemContainerGenerator_StatusChanged(object sender, EventArgs e)
        {
            SetCurrentlyPlayedTrack();
            SetCurrentMessages();
        }

        private void SetCurrentlyPlayedTrack()
        {
            int currentPlayingIndex = QueueApp.QueueManager.PlayingQueue.GetCurrentTrackIndex();
            if (currentPlayingIndex < 0)
                currentPlayingIndex = 0;

            ItemContainerGenerator generator = PrimeView.TrackView.ItemContainerGenerator;

            switch (generator.Status)
            {
                case System.Windows.Controls.Primitives.GeneratorStatus.ContainersGenerated:
                    if (currentPlayingIndex < PrimeView.TrackView.Items.Count &&
                        PlayerApp.PlaybackManager.State != Domain.Playing.PlaybackManager.PlayerState.Stopped)
                    {
                        if (SongSharpApp.States.CurrentPlaylist != null)
                        {
                            switch (SongSharpApp.States.CurrentPlaylist.PlaylistType)
                            {
                                case Playlist.AutoGeneratedPlaylistType.Now_playing:
                                    ListViewItem playingItem = PrimeView.TrackView.ItemContainerGenerator.
                                        ContainerFromIndex(currentPlayingIndex) as ListViewItem;

                                    // Workaround to display old played items in the original color
                                    try
                                    {
                                        (PrimeView.TrackView.ItemContainerGenerator.
                                            ContainerFromIndex(currentPlayingIndex + 1) as ListViewItem).Foreground = Brushes.Black;
                                    }
                                    catch (Exception) { }
                                    try
                                    {
                                        (PrimeView.TrackView.ItemContainerGenerator.
                                            ContainerFromIndex(currentPlayingIndex - 1) as ListViewItem).Foreground = Brushes.Black;
                                    }
                                    catch (Exception) { }
                                    // Workaround end

                                    if (playingItem != null)
                                    {
                                        playingItem.Foreground = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#FF24736B"));
                                       
                                    }
                                    break;
                            }
                        }
                    }
                    break;
            }
        }

        private void SetCurrentMessages()
        {
            if (PrimeView.TrackView.Items.Count == 0)
                TrackViewMessage = Settings.Default.MessageEmptyPlaylist;
            else
                TrackViewMessage = null;
        }

        // Properties accesssed by view PrimeView for databinding
        #region Databinding Properties

        public string TrackViewMessage
        {
            get { return m_trackViewMessage; }
            set
            {
                m_trackViewMessage = value;
                OnPropertyChanged("TrackViewMessage");
                OnPropertyChanged("DisplayTrackViewMessage");
            }
        }

        public bool DisplayTrackViewMessage
        {
            get
            {
                if (TrackViewMessage == null || TrackViewMessage.Length == 0)
                    return false;
                return true;
            }
        }

        public double TrackPosition
        {
            get { return m_trackPosition; }
            set
            {
                m_trackPosition = value;
                OnPropertyChanged("TrackPosition");
            }
        }

        public double CurrentVolume
        {
            get { return m_currentVolume; }
            set {
                m_currentVolume = value;
                OnPropertyChanged("CurrentVolume");
            }
        }

        public string CurrentTrackInfo
        {
            get { return m_currentTrackInfo; }
            set
            {
                m_currentTrackInfo = value;
                OnPropertyChanged("CurrentTrackInfo");
            }
        }

        public ObservableCollection<PlaylistViewData> Playlists
        {
            get
            {
                return m_playlist;
            }
            set
            {
                m_playlist = value;
                OnPropertyChanged("Playlists");
            }
        }
        public ObservableCollection<TrackViewData> TrackList
        {
            get
            {
                if (m_trackList != null)
                    foreach (TrackViewData t in m_trackList)
                        if (t.Dirty)
                        {
                            try
                            {
                                TagCrawler.RunWorkerAsync(m_trackList);
                            }
                            catch (InvalidOperationException) { }
                            break;
                        }
                return m_trackList;
            }
            set
            {
                m_trackList = value;
                OnPropertyChanged("TrackList");
            }
        }
        #endregion

        //public ICommand ClearContactBookCommand
        //{
        //    get
        //    {
        //        if (clearContactBookCommand == null)
        //        {
        //            clearContactBookCommand = new DelegateCommand(ClearContactBook, CanClearContactBook);
        //        }
        //        return clearContactBookCommand;
        //    }
        //}

        // Commands invoked by view PrimView
        #region CommandProperties

        public ICommand ShowStatisticCommand
        {
            get
            {
                if (m_showStatisticCommand == null)
                    m_showStatisticCommand = new DelegateCommand(ShowStatistic);
                return m_showStatisticCommand;
            }
        }

        public ICommand PlayTrackCommand
        {
            get
            {
                if (m_playTrackCommand == null)
                    m_playTrackCommand = new DelegateCommand(PlayTrack);
                return m_playTrackCommand;
            }
        }
        public ICommand EnqueueTrackCommand
        {
            get
            {
                if (m_enqueueTrackCommand == null)
                    m_enqueueTrackCommand = new DelegateCommand(EnqueueTrack);
                return m_enqueueTrackCommand;
            }
        }
        #endregion

        private void LoadTracklist(Playlist playlist)
        {
            m_trackList = new ObservableCollection<TrackViewData>();

            switch (playlist.PlaylistType)
            {
                case Playlist.AutoGeneratedPlaylistType.All_music:
                    playlist.Tracks = Track.GetAll();
                    break;
            }

            if (playlist.Tracks.Count > 0)
                foreach (Track m in playlist.Tracks)
                    m_trackList.Add(new TrackViewData(m));
            UpdateTrackList();
        }

        private void LoadPlaylists()
        {
            m_playlist = new ObservableCollection<PlaylistViewData>();

            foreach (Playlist p in PlaylistApp.GetEntirePlaylists(false))
                switch (p.PlaylistType) {
                    case Playlist.AutoGeneratedPlaylistType.Now_playing:
                        m_playlist.Add(new PlaylistViewData(p, Settings.Default.PlaylistIconBlue));
                        break;
                    case Playlist.AutoGeneratedPlaylistType.All_music:
                        m_playlist.Add(new PlaylistViewData(p, Settings.Default.PlaylistIconBlack));
                        break;
                    case Playlist.AutoGeneratedPlaylistType.Most_played:
                        m_playlist.Add(new PlaylistViewData(p, Settings.Default.PlaylistIconBlack));
                        break;
                    case Playlist.AutoGeneratedPlaylistType.Least_played:
                        m_playlist.Add(new PlaylistViewData(p, Settings.Default.PlaylistIconBlack));
                        break;
                    case Playlist.AutoGeneratedPlaylistType.Recently_added:
                        m_playlist.Add(new PlaylistViewData(p, Settings.Default.PlaylistIconBlack));
                        break;
                    default:
                        m_playlist.Add(new PlaylistViewData(p));
                        break;
                }

            m_playlist.Add(new PlaylistViewData(new Playlist("Add new playlist"), Settings.Default.PlaylistIconAdd));
            Playlists = m_playlist;
            OnPropertyChanged("Playlist");
        }

        internal void UpdateTrackList()
        {
            TrackList = new ObservableCollection<TrackViewData>(TrackList);
            OnPropertyChanged("TrackList");
        }

        //private bool CanClearContactBook()
        //{
        //    return contacts.Count > 0;
        //}

        //private void ClearContactBook()
        //{
        //    contacts.Clear();
        //}

        // Methods used for command delegates and interface implementations
        private void EnqueueTrack()
        {
            QueueApp.Enqueue(SongSharpApp.States.CurrentTrack);
        }

        private void PlayTrack()
        {
            QueueApp.InsertToNext(SongSharpApp.States.CurrentTrack);
            PlayerApp.Basic.PlayNext(TrackHandlingType.SkippedTrack);
        }

        private void ShowStatistic()
        {
            if (SongSharpApp.States.CurrentTrack != null)
                StatisticViewModel.ShowWindow(SongSharpApp.States.CurrentTrack);
        }

        #region Interface implementation of PrimeView
        public void Tracklist_Click(object sender, EventArgs e)
        {
            System.Windows.Controls.ListView view = (System.Windows.Controls.ListView)sender;
            if (view.SelectedItem != null)
            {
                SongSharpApp.States.CurrentTrack = ((TrackViewData)view.SelectedItem).TrackObject;
            }
            else
            {
                SongSharpApp.States.CurrentTrack = null;
            }
        }

        public void Playlist_Click(object sender, EventArgs e)
        {
            System.Windows.Controls.ListBox box = (System.Windows.Controls.ListBox)sender;
            if (box.SelectedItem != null)
            {
                PlaylistViewData playlistViewData = (PlaylistViewData)box.SelectedItem;

                try
                {
                    List<Track> list = playlistViewData.PlaylistObject.Tracks;
                    playlistViewData.PlaylistObject.Tracks = PlaylistApp.LoadTracks(playlistViewData.PlaylistObject);
                    if (playlistViewData.PlaylistObject.Tracks.Count == 0 && list.Count == 0)
                    {
                        TrackViewMessage = Settings.Default.MessageEmptyPlaylist;
                    }
                }
                catch (System.IO.FileNotFoundException)
                {
                    TrackViewMessage = Settings.Default.MessagePlaylistNotFound;
                }
                catch (ArgumentNullException)
                {
                    if (playlistViewData.PlaylistObject.Name == "Add new playlist")
                    {
                        EditPlaylistView editPlaylistView = new EditPlaylistView();
                        editPlaylistView.Owner = PrimeView;
                        editPlaylistView.ShowDialog();
                        return;
                    }
                }
                catch (Exception)
                {

                }

                SongSharpApp.States.CurrentPlaylist = playlistViewData.PlaylistObject;
                LoadTracklist(playlistViewData.PlaylistObject);
            }
        }

        // Routed method for a double click event on a track
        public void Tracklist_DoubleClick(object sender, EventArgs e)
        {
            System.Windows.Controls.ListView view = (System.Windows.Controls.ListView)sender;
            SongSharpApp.States.CurrentTrack = ((TrackViewData)view.SelectedItem).TrackObject;
            switch (SongSharpApp.States.CurrentPlaylist.PlaylistType)
            {
                case Playlist.AutoGeneratedPlaylistType.Now_playing:
                    // do nothing
                    break;
                default:
                    QueueApp.Enqueue(SongSharpApp.States.CurrentTrack);
                    break;
            }
        }

        // Routed method for a double click event on a playlist
        public void Playlist_DoubleClick(object sender, EventArgs e)
        {
            System.Windows.Controls.ListBox box = (System.Windows.Controls.ListBox)sender;
            PlaylistViewData playlist = (PlaylistViewData)box.SelectedItem;
            SongSharpApp.States.CurrentPlaylist = playlist.PlaylistObject;
        }

        public void OnClose(object sender, EventArgs e)
        {
            SongSharpApp.OnClose(sender, e);
        }

        public void Tracklist_Drop(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(DataFormats.FileDrop) && 
                SongSharpApp.States.CurrentPlaylist != null)
            {
                string[] files = e.Data.GetData(DataFormats.FileDrop, true) as string[];
                List<Track> tracks = LibraryApp.AddTracksToLibrary(files);
                SongSharpApp.States.CurrentPlaylist.Tracks.AddRange(tracks);
                LoadTracklist(SongSharpApp.States.CurrentPlaylist);
            } 
        }

        public void PlayerNextBtn_Click(object sender, EventArgs e)
        {
            PlayerApp.Basic.PlayNext(TrackHandlingType.SkippedTrack);
        }

        public void PlayerPreviousBtn_Click(object sender, EventArgs e)
        {
            PlayerApp.Basic.PlayPrevious();
        }

        public void PlayerPauseBtn_Click(object sender, EventArgs e)
        {
            PlayerApp.Basic.Pause();
        }

        public void Playlist_Drop(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(DataFormats.FileDrop))
            {
                string[] files = e.Data.GetData(DataFormats.FileDrop, true) as string[];
                LibraryApp.AddPlaylistsToLibrary(files);
                LoadPlaylists();
            }
        }

        public void PlayerTrackbar_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            if (!SongTrackerIsAutomaticallyMoved)
                PlayerApp.Basic.Seek(e.NewValue);
            SongTrackerIsAutomaticallyMoved = false;
        }


        public void PlayerVolume_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            PlayerApp.Basic.VolumeChanged(sender, e.NewValue);
        }
        
        public void PlayerSuggestMusicStream_Click(object sender, EventArgs e)
        {
            ThinkViewModel.ShowWindow();
        }

        public void MusicStream_created(object sender, EventArgs e)
        {
            PrimeView.PlaylistView.SelectedIndex = 0;
            Playlist_Click(PrimeView.PlaylistView, e);

            PlayerApp.Basic.PlayNext(TrackHandlingType.SkippedTrack);
        }

        public void PlayerOpenLogWindow_Click(object sender, EventArgs e)
        {
            LogViewModel.ShowWindow();
        }
        #endregion
    }
}